home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / nethack.lha / nethack-3.1 / src / mail.c < prev    next >
C/C++ Source or Header  |  1993-01-19  |  15KB  |  585 lines

  1. /*    SCCS Id: @(#)mail.c    3.1    92/11/14    */
  2. /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
  3. /* NetHack may be freely redistributed.  See license for details. */
  4.  
  5. #include "hack.h"
  6.  
  7. #ifdef MAIL
  8. #include "mail.h"
  9. /*
  10.  * Notify user when new mail has arrived.  Idea by Merlyn Leroy.
  11.  *
  12.  * The mail daemon can move with less than usual restraint.  It can:
  13.  *    - move diagonally from a door
  14.  *    - use secret and closed doors
  15.  *    - run through a monster ("Gangway!", etc.)
  16.  *    - run over pools & traps
  17.  *
  18.  * Possible extensions:
  19.  *    - Open the file MAIL and do fstat instead of stat for efficiency.
  20.  *      (But sh uses stat, so this cannot be too bad.)
  21.  *    - Examine the mail and produce a scroll of mail named "From somebody".
  22.  *    - Invoke MAILREADER in such a way that only this single letter is read.
  23.  *    - Do something to the text when the scroll is enchanted or cancelled.
  24.  *    - Make the daemon always appear at a stairwell, and have it find a
  25.  *      path to the hero.
  26.  *
  27.  * Note by Olaf Seibert: On the Amiga, we usually don't get mail.  So we go
  28.  *             through most of the effects at 'random' moments.
  29.  */
  30.  
  31. static boolean FDECL(md_start,(coord *));
  32. static boolean FDECL(md_stop,(coord *, coord *));
  33. static boolean FDECL(md_rush,(struct monst *,int,int));
  34. static void FDECL(newmail, (struct mail_info *));
  35.  
  36. extern char *viz_rmin, *viz_rmax;    /* line-of-sight limits (vision.c) */
  37.  
  38. #ifdef OVL0
  39.  
  40. # if !defined(UNIX) && !defined(VMS)
  41. int mustgetmail = -1;
  42. # endif
  43.  
  44. #endif /* OVL0 */
  45. #ifdef OVLB
  46.  
  47. # ifdef UNIX
  48. #  include <sys/stat.h>
  49. #  include <pwd.h>
  50. /* DON'T trust all Unices to declare getpwuid() in <pwd.h> */
  51. #  if !defined(_BULL_SOURCE) && !defined(sgi)
  52. /* DO trust all SVR4 to typedef uid_t in <sys/types.h> (probably to a long) */
  53. #   if defined(POSIX_TYPES) || defined(SVR4)
  54. extern struct passwd *FDECL(getpwuid,(uid_t));
  55. #   else 
  56. extern struct passwd *FDECL(getpwuid,(int));
  57. #   endif
  58. #  endif
  59. static struct stat omstat,nmstat;
  60. static char *mailbox = NULL;
  61. static long laststattime;
  62.  
  63. # ifdef AMS                /* Just a placeholder for AMS */
  64. #   define MAILPATH "/dev/null"
  65. # else
  66. #  if defined(BSD) || defined(ULTRIX)
  67. #   define MAILPATH "/usr/spool/mail/"
  68. #  endif
  69. #  if defined(SYSV) || defined(HPUX)
  70. #   define MAILPATH "/usr/mail/"
  71. #  endif
  72. # endif /* AMS */
  73.  
  74. void
  75. getmailstatus()
  76. {
  77.     if(!mailbox && !(mailbox = getenv("MAIL"))) {
  78. #  ifdef MAILPATH
  79. #   ifdef AMS
  80.             struct passwd ppasswd;
  81.  
  82.         (void) memcpy(&ppasswd, getpwuid(getuid()), sizeof(struct passwd));
  83.         if (ppasswd.pw_dir) {
  84.              mailbox = (char *) alloc((unsigned) strlen(ppasswd.pw_dir)+sizeof(AMS_MAILBOX));
  85.              Strcpy(mailbox, ppasswd.pw_dir);
  86.              Strcat(mailbox, AMS_MAILBOX);
  87.         } else
  88.           return;
  89. #   else
  90.         mailbox = (char *) alloc(sizeof(MAILPATH)+8);
  91.         Strcpy(mailbox, MAILPATH);
  92.         Strcat(mailbox, getpwuid(getuid())->pw_name);
  93. #  endif /* AMS */
  94. #  else
  95.         return;
  96. #  endif
  97.     }
  98.     if(stat(mailbox, &omstat)){
  99. #  ifdef PERMANENT_MAILBOX
  100.         pline("Cannot get status of MAIL=\"%s\".", mailbox);
  101.         mailbox = 0;
  102. #  else
  103.         omstat.st_mtime = 0;
  104. #  endif
  105.     }
  106. }
  107. # endif /* UNIX */
  108.  
  109. /*
  110.  * Pick coordinates for a starting position for the mail daemon.  Called
  111.  * from newmail() and newphone().
  112.  */
  113. static boolean
  114. md_start(startp)
  115.     coord *startp;
  116. {
  117.     coord testcc;    /* scratch coordinates */
  118.     int row;        /* current row we are checking */
  119.     int lax;        /* if TRUE, pick a position in sight. */
  120.     int dd;        /* distance to current point */
  121.     int max_distance;    /* max distance found so far */
  122.  
  123.     /*
  124.      * If blind and not telepathic, then it doesn't matter what we pick ---
  125.      * the hero is not going to see it anyway.  So pick a nearby position.
  126.      */
  127.     if (Blind && !Telepat) {
  128.     if (!enexto(startp, u.ux, u.uy, (struct permonst *) 0))
  129.         return FALSE;    /* no good posiitons */
  130.     return TRUE;
  131.     }
  132.  
  133.     /*
  134.      * Arrive at an up or down stairwell if it is in line of sight from the
  135.      * hero.
  136.      */
  137.     if (couldsee(upstair.sx, upstair.sy)) {
  138.     startp->x = upstair.sx;
  139.     startp->y = upstair.sy;
  140.     return TRUE;
  141.     }
  142.     if (couldsee(dnstair.sx, dnstair.sy)) {
  143.     startp->x = dnstair.sx;
  144.     startp->y = dnstair.sy;
  145.     return TRUE;
  146.     }
  147.  
  148.     /*
  149.      * Try to pick a location out of sight next to the farthest position away
  150.      * from the hero.  If this fails, try again, just picking the farthest
  151.      * position that could be seen.  What we really ought to be doing is
  152.      * finding a path from a stairwell...
  153.      *
  154.      * The arrays viz_rmin[] and viz_rmax[] are set even when blind.  These
  155.      * are the LOS limits for each row.
  156.      */
  157.     lax = 0;    /* be picky */
  158.     max_distance = -1;
  159. retry:
  160.     for (row = 0; row < ROWNO; row++) {
  161.     if (viz_rmin[row] < viz_rmax[row]) {
  162.         /* There are valid positions on this row. */
  163.         dd = distu(viz_rmin[row],row);
  164.         if (dd > max_distance) {
  165.         if (lax) {
  166.             max_distance = dd;
  167.             startp->y = row;
  168.             startp->x = viz_rmin[row];
  169.             
  170.         } else if (enexto(&testcc, (xchar)viz_rmin[row], row,
  171.                         (struct permonst *) 0) &&
  172.                !cansee(testcc.x, testcc.y) &&
  173.                couldsee(testcc.x, testcc.y)) {
  174.             max_distance = dd;
  175.             *startp = testcc;
  176.         }
  177.         }
  178.         dd = distu(viz_rmax[row],row);
  179.         if (dd > max_distance) {
  180.         if (lax) {
  181.             max_distance = dd;
  182.             startp->y = row;
  183.             startp->x = viz_rmax[row];
  184.             
  185.         } else if (enexto(&testcc, (xchar)viz_rmax[row], row,
  186.                         (struct permonst *) 0) &&
  187.                !cansee(testcc.x,testcc.y) &&
  188.                couldsee(testcc.x, testcc.y)) {
  189.  
  190.             max_distance = dd;
  191.             *startp = testcc;
  192.         }
  193.         }
  194.     }
  195.     }
  196.  
  197.     if (max_distance < 0) {
  198.     if (!lax) {
  199.         lax = 1;        /* just find a position */
  200.         goto retry;
  201.     }
  202.     return FALSE;
  203.     }
  204.  
  205.     return TRUE;
  206. }
  207.  
  208. /*
  209.  * Try to choose a stopping point as near as possible to the starting
  210.  * position while still adjacent to the hero.  If all else fails, try
  211.  * enexto().  Use enexto() as a last resort because enexto() chooses
  212.  * its point randomly, which is not what we want.
  213.  */
  214. static boolean
  215. md_stop(stopp, startp)
  216.     coord *stopp;    /* stopping position (we fill it in) */
  217.     coord *startp;    /* starting positon (read only) */
  218. {
  219.     int x, y, distance, min_distance = -1;
  220.  
  221.     for (x = u.ux-1; x <= u.ux+1; x++)
  222.     for (y = u.uy-1; y <= u.uy+1; y++) {
  223.         if (!isok(x, y) || (x == u.ux && y == u.uy)) continue;
  224.  
  225.         if (ACCESSIBLE(levl[x][y].typ) && !MON_AT(x,y)) {
  226.         distance = dist2(x,y,startp->x,startp->y);
  227.         if (min_distance < 0 || distance < min_distance ||
  228.             (distance == min_distance && rn2(2))) {
  229.             stopp->x = x;
  230.             stopp->y = y;
  231.             min_distance = distance;
  232.         }
  233.         }
  234.     }
  235.  
  236.     /* If we didn't find a good spot, try enexto(). */
  237.     if (min_distance < 0 &&
  238.         !enexto(stopp, u.ux, u.uy, &mons[PM_MAIL_DAEMON]))
  239.     return FALSE;
  240.  
  241.     return TRUE;
  242. }
  243.  
  244. /* Let the mail daemon have a larger vocabulary. */
  245. static const char NEARDATA *mail_text[] = {
  246.     "Gangway!",
  247.     "Look out!",
  248.     "Pardon me!"
  249. };
  250. #define md_exclamations()    (mail_text[rn2(3)])
  251.  
  252. /*
  253.  * Make the mail daemon run through the dungeon.  The daemon will run over
  254.  * any monsters that are in its path, but will replace them later.  Return
  255.  * FALSE if the md gets stuck in a position where there is a monster.  Return
  256.  * TRUE otherwise.
  257.  */
  258. static boolean
  259. md_rush(md,tx,ty)
  260.     struct monst *md;
  261.     register int tx, ty;        /* destination of mail daemon */
  262. {
  263.     struct monst *mon;            /* displaced monster */
  264.     register int dx, dy;        /* direction counters */
  265.     int fx = md->mx, fy = md->my;    /* current location */
  266.     int nfx = fx, nfy = fy,        /* new location */ 
  267.     d1, d2;                /* shortest distances */
  268.  
  269.     /*
  270.      * It is possible that the monster at (fx,fy) is not the md when:
  271.      * the md rushed the hero and failed, and is now starting back.
  272.      */
  273.     if (m_at(fx, fy) == md) {
  274.     remove_monster(fx, fy);        /* pick up from orig position */
  275.     newsym(fx, fy);
  276.     }
  277.  
  278.     /*
  279.      * At the beginning and exit of this loop, md is not placed in the
  280.      * dungeon.
  281.      */
  282.     while (1) {
  283.     /* Find a good location next to (fx,fy) closest to (tx,ty). */
  284.     d1 = dist2(fx,fy,tx,ty);
  285.     for (dx = -1; dx <= 1; dx++) for(dy = -1; dy <= 1; dy++)
  286.         if ((dx || dy) && isok(fx+dx,fy+dy) && 
  287.                        !IS_STWALL(levl[fx+dx][fy+dy].typ)) {
  288.         d2 = dist2(fx+dx,fy+dy,tx,ty);
  289.         if (d2 < d1) {
  290.             d1 = d2;
  291.             nfx = fx+dx;
  292.             nfy = fy+dy;
  293.         }
  294.         }
  295.  
  296.     /* Break if the md couldn't find a new position. */
  297.     if (nfx == fx && nfy == fy) break;
  298.  
  299.     fx = nfx;            /* this is our new position */
  300.     fy = nfy;
  301.  
  302.     /* Break if the md reaches its destination. */
  303.     if (fx == tx && fy == ty) break;
  304.  
  305.     if ((mon = m_at(fx,fy)) != 0)    /* save monster at this position */
  306.         verbalize(md_exclamations());
  307.     else if (fx == u.ux && fy == u.uy)
  308.         verbalize("Excuse me.");
  309.  
  310.     place_monster(md,fx,fy);    /* put md down */
  311.     newsym(fx,fy);            /* see it */
  312.     flush_screen(0);        /* make sure md shows up */
  313.     delay_output();            /* wait a little bit */
  314.  
  315.     /* Remove md from the dungeon.  Restore original mon, if necessary. */
  316.     if (mon) {
  317.         if ((mon->mx != fx) || (mon->my != fy))
  318.         place_worm_seg(mon, fx, fy);
  319.         else
  320.         place_monster(mon, fx, fy);
  321.     } else
  322.         remove_monster(fx, fy);
  323.     newsym(fx,fy);
  324.     }
  325.  
  326.     /*
  327.      * Check for a monster at our stopping position (this is possible, but
  328.      * very unlikely).  If one exists, then have the md leave in disgust.
  329.      */
  330.     if ((mon = m_at(fx, fy)) != 0) {
  331.     place_monster(md, fx, fy);    /* display md with text below */
  332.     newsym(fx, fy);
  333.     verbalize("This place's too crowded.  I'm outta here.");
  334.  
  335.     if ((mon->mx != fx) || (mon->my != fy))    /* put mon back */
  336.         place_worm_seg(mon, fx, fy);
  337.     else
  338.         place_monster(mon, fx, fy);
  339.  
  340.     newsym(fx, fy);
  341.     return FALSE;
  342.     }
  343.  
  344.     place_monster(md, fx, fy);    /* place at final spot */
  345.     newsym(fx, fy);
  346.     flush_screen(0);
  347.     delay_output();            /* wait a little bit */
  348.  
  349.     return TRUE;
  350. }
  351.  
  352. /* Deliver a scroll of mail. */
  353. /*ARGSUSED*/
  354. static void
  355. newmail(info)
  356. struct mail_info *info;
  357. {
  358.     struct monst *md;
  359.     coord start, stop;
  360.     boolean message_seen = FALSE;
  361.  
  362.     /* Try to find good starting and stopping places. */
  363.     if (!md_start(&start) || !md_stop(&stop,&start)) goto give_up;
  364.  
  365.     /* Make the daemon.  Have it rush towards the hero. */
  366.     if (!(md = makemon(&mons[PM_MAIL_DAEMON], start.x, start.y))) goto give_up;
  367.     if (!md_rush(md, stop.x, stop.y)) goto go_back;
  368.  
  369.     message_seen = TRUE;
  370. # ifdef NO_MAILREADER
  371.     verbalize("Hello, %s!  You have some mail in the outside world.", plname);
  372. # else
  373.     verbalize("Hello, %s!  %s.", plname, info->display_txt);
  374.  
  375.     if (info->message_typ) {
  376.     struct obj *obj = mksobj(SCR_MAIL, FALSE, FALSE);
  377.     if (distu(md->mx,md->my) > 2)
  378.         verbalize("Catch!");
  379.     display_nhwindow(WIN_MESSAGE, FALSE);
  380.     if (info->object_nam) {
  381.         obj = oname(obj, info->object_nam, FALSE);
  382.         if (info->response_cmd) {    /*(hide extension of the obj name)*/
  383.         int namelth = info->response_cmd - info->object_nam - 1;
  384.         if ( namelth <= 0 || namelth >= (int) obj->onamelth )
  385.             impossible("mail delivery screwed up");
  386.         else
  387.             *(ONAME(obj) + namelth) = '\0';
  388.         /* Note: renaming object will discard the hidden command. */
  389.         }
  390.     }
  391.     obj = hold_another_object(obj, "Oops!",
  392.                   (const char *)0, (const char *)0);
  393.     }
  394. # endif /* NO_MAILREADER */
  395.  
  396.     /* zip back to starting location */
  397. go_back:
  398.     (void) md_rush(md, start.x, start.y);
  399.     mongone(md);
  400.     /* deliver some classes of messages even if no daemon ever shows up */
  401. give_up:
  402.     if (!message_seen && info->message_typ == MSG_OTHER)
  403.     pline("Hark!  \"%s.\"", info->display_txt);
  404. }
  405.  
  406. #endif /* OVLB */
  407.  
  408. # if !defined(UNIX) && !defined(VMS)
  409.  
  410. #ifdef OVL0
  411.  
  412. void
  413. ckmailstatus()
  414. {
  415.     if (u.uswallow) return;
  416.     if (mustgetmail < 0) {
  417. #ifdef AMIGA
  418.         mustgetmail=(moves<2000)?(100+rn2(2000)):(2000+rn2(3000));
  419. #endif
  420.         return;
  421.     }
  422.     if (--mustgetmail <= 0) {
  423.         static struct mail_info
  424.             deliver = {MSG_MAIL,"I have some mail for you",0,0};
  425.         newmail(&deliver);
  426.         mustgetmail = -1;
  427.     }
  428. }
  429.  
  430. #endif /* OVL0 */
  431. #ifdef OVLB
  432.  
  433. /*ARGSUSED*/
  434. void
  435. readmail(otmp)
  436. struct obj *otmp;
  437. {
  438. #ifdef AMIGA
  439.     char *junk[]={
  440.     "It reads:  \"Please disregard previous letter.\"",
  441.     "It reads:  \"Welcome to NetHack 3.1!\"",
  442.     "It reads:  \"Only Amiga makes it possible.\"",
  443.     "It reads:  \"CATS have all the answers.\"",
  444.     "It reads:  \"Report bugs to nethack-bugs@linc.cis.upenn.edu\""
  445.     };
  446.  
  447.     pline(junk[rn2(SIZE(junk))]);
  448. #else
  449.     pline("It reads:  \"Please disregard previous letter.\"");
  450. #endif
  451. }
  452.  
  453. #endif /* OVLB */
  454.  
  455. # endif /* !UNIX && !VMS */
  456.  
  457. # ifdef UNIX
  458.  
  459. #ifdef OVL0
  460.  
  461. void
  462. ckmailstatus()
  463. {
  464.     if(!mailbox || u.uswallow
  465. #  ifdef MAILCKFREQ
  466.             || moves < laststattime + MAILCKFREQ
  467. #  endif
  468.                             )
  469.         return;
  470.  
  471.     laststattime = moves;
  472.     if(stat(mailbox, &nmstat)){
  473. #  ifdef PERMANENT_MAILBOX
  474.         pline("Cannot get status of MAIL=\"%s\" anymore.", mailbox);
  475.         mailbox = 0;
  476. #  else
  477.         nmstat.st_mtime = 0;
  478. #  endif
  479.     } else if(nmstat.st_mtime > omstat.st_mtime) {
  480.         if(nmstat.st_size) {
  481.             static struct mail_info
  482.                 deliver = {MSG_MAIL,"I have some mail for you",0,0};
  483.             newmail(&deliver);
  484.         }
  485.         getmailstatus();    /* might be too late ... */
  486.     }
  487. }
  488.  
  489. #endif /* OVL0 */
  490.  
  491. #ifdef OVLB
  492.  
  493. /*ARGSUSED*/
  494. void
  495. readmail(otmp)
  496. struct obj *otmp;
  497. {
  498. #  ifdef DEF_MAILREADER            /* This implies that UNIX is defined */
  499.     register const char *mr = 0;
  500.  
  501.     display_nhwindow(WIN_MESSAGE, FALSE);
  502.     if(!(mr = getenv("MAILREADER")))
  503.         mr = DEF_MAILREADER;
  504.  
  505.     if(child(1)){
  506.         (void) execl(mr, mr, NULL);
  507.         terminate(1);
  508.     }
  509. #  else
  510. #   ifndef AMS              /* AMS mailboxes are directories */
  511.     display_file(mailbox, TRUE);
  512. #   endif /* AMS */
  513. #  endif /* DEF_MAILREADER */
  514.  
  515.     /* get new stat; not entirely correct: there is a small time
  516.        window where we do not see new mail */
  517.     getmailstatus();
  518. }
  519.  
  520. #endif /* OVLB */
  521.  
  522. # endif /* UNIX */
  523.  
  524. # ifdef VMS
  525.  
  526. #ifdef OVL0
  527.  
  528. volatile int broadcasts = 0;
  529.  
  530. void
  531. ckmailstatus()
  532. {
  533.     struct mail_info *brdcst, *parse_next_broadcast();
  534.  
  535.     if(u.uswallow) return;
  536.  
  537.     while (broadcasts > 0) {    /* process all trapped broadcasts [until] */
  538.     broadcasts--;
  539.     if ((brdcst = parse_next_broadcast()) != 0) {
  540.         newmail(brdcst);
  541.         break;        /* only handle one real message at a time */
  542.     }
  543.     }
  544. }
  545.  
  546. #endif /* OVL0 */
  547.  
  548. #ifdef OVLB
  549.  
  550. void
  551. readmail(otmp)
  552. struct obj *otmp;
  553. {
  554. #  ifdef SHELL    /* can't access mail reader without spawning subprocess */
  555.     char *p, *cmd, buf[BUFSZ], qbuf[BUFSZ];
  556.  
  557.     /* there should be a command hidden beyond the object name */
  558.     p = otmp->onamelth ? ONAME(otmp) : "";
  559.     cmd = (strlen(p) + 1 < otmp->onamelth) ? eos(p) + 1 : (char *) 0;
  560.     if (!cmd || !*cmd) cmd = "SPAWN";
  561.  
  562.     Sprintf(qbuf, "System command (%s)", cmd);
  563.     getlin(qbuf, buf);
  564.     clear_nhwindow(WIN_MESSAGE);
  565.     if (*buf != '\033') {
  566.     for (p = eos(buf); p > buf; *p = '\0')
  567.         if (*--p != ' ') break;    /* strip trailing spaces */
  568.     if (*buf) cmd = buf;        /* use user entered command */
  569.     if (!strcmpi(cmd, "SPAWN") || !strcmp(cmd, "!"))
  570.         cmd = (char *) 0;        /* interactive escape */
  571.  
  572.     vms_doshell(cmd, TRUE);
  573.     (void) sleep(1);
  574.     }
  575. #  endif /* SHELL */
  576. }
  577.  
  578. #endif /* OVLB */
  579.  
  580. # endif /* VMS */
  581.  
  582. #endif /* MAIL */
  583.  
  584. /*mail.c*/
  585.